/**
 * Copyright (c) 2011 Basil Shikin, VintageRadio Project
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.vintagephone.model.hardware.ioio;

import ioio.lib.api.AnalogInput;
import ioio.lib.api.DigitalInput;
import ioio.lib.api.DigitalInput.Spec.Mode;
import ioio.lib.api.exception.ConnectionLostException;

import org.vintagephone.model.hardware.RadioHardwareListener;

import android.util.Log;

public class IOIORadioHardwareThread
    extends IOIOThreadBase
{
    private static final String TAG = "IOIORadioHardwareThread";

    private static final float VOLUME_CHANGE_THRESHOLD = 0.05F;

    private static final int SKIP_VOLUME_COUNT = 100;
    private static final int VOLUME_READ_ATTEMPTS = 10;
    
    // Sensors
    private AnalogInput    m_volumeSensor;
    
    private DigitalInput   m_onSensor;
    
    private DigitalInput[] m_buttonSensors;
    
    private DigitalInput   m_rotASensor;
    private DigitalInput   m_rotBSensor;
    
    // States
    private float          m_volume;
    private boolean        m_isOn;
    private boolean[]      m_buttonStates;
    private boolean        m_rotAOn;
    private boolean        m_rotBOn;
    private int            m_rotPosition;
    
    private int            m_skipVolumeCount;            
    
    // Listeners
    private RadioHardwareListener m_hardwareListener;
    private DebugListener         m_debugListener;
    
    public void setListener(RadioHardwareListener listener)
    {
        m_hardwareListener = listener;
        if ( m_hardwareListener != null )
        {
            m_hardwareListener.onStateChanged( m_isOn );
            m_hardwareListener.volumeChanged( m_volume );
        }
    }

    public void setDebugListener(DebugListener debugListener)
    {
        m_debugListener = debugListener;
    }
    
    @Override
    protected void setup() throws ConnectionLostException
    {
        super.setup();

        
        m_onSensor = m_ioio.openDigitalInput( 4, Mode.PULL_DOWN );
        
        m_volumeSensor = m_ioio.openAnalogInput( 33 );
        
        m_buttonSensors = new DigitalInput[4];
        m_buttonSensors[0] = m_ioio.openDigitalInput( 11, Mode.PULL_UP );
        m_buttonSensors[1] = m_ioio.openDigitalInput( 12, Mode.PULL_UP );
        m_buttonSensors[2] = m_ioio.openDigitalInput( 13, Mode.PULL_UP );
        m_buttonSensors[3] = m_ioio.openDigitalInput( 14, Mode.PULL_UP );
        
        m_buttonStates = new boolean[4];
        for ( int i = 0; i < m_buttonStates.length; i++ ) m_buttonStates[i] = true; // All on by default
        
        m_rotASensor = m_ioio.openDigitalInput( 5, Mode.PULL_UP );
        m_rotBSensor = m_ioio.openDigitalInput( 6, Mode.PULL_UP );
    }

    @Override
    protected void loop() throws ConnectionLostException
    {
        boolean stateChanged = false;
        
        try
        {
            stateChanged |= processOnButton();
            
            if ( m_isOn )
            {
                stateChanged |= processSongRot();
                stateChanged |= processVolumePot();
                stateChanged |= processStationButtons();
                
                Thread.sleep( 5 );
            }
            else
            {
                Thread.sleep( 50 );
            }
        }
        catch ( ConnectionLostException e)
        {
            if ( m_isOn )
            {
                m_isOn = false;
                if ( m_hardwareListener != null ) m_hardwareListener.onStateChanged( false );
            }
            
            throw e;
        }
        catch (InterruptedException e) 
        { 
            return; 
        }

        
        if ( stateChanged )
        {
            final String hardwareState = buildStateString();
            if ( m_debugListener != null ) m_debugListener.printDebugMessage( "Device state: " + hardwareState );
            
            Log.d( TAG, "Device state: " + hardwareState );
        }
    }
    

    private boolean processStationButtons() throws InterruptedException, ConnectionLostException
    {
        boolean stateChanged = false;
        
        for ( int i = 0; i < m_buttonStates.length; i++ )
        {
            final boolean newState = m_buttonSensors[i].read();
            if ( newState != m_buttonStates[i] )
            {
                stateChanged = true;
                m_buttonStates[i] = newState;
                
                // If button is pressed
                if ( newState )
                {
                    if ( m_hardwareListener != null ) m_hardwareListener.buttonPressed( i );
                }
            }
        }
        
        return stateChanged;
    }

    private boolean processVolumePot() throws InterruptedException, ConnectionLostException
    {
        boolean stateChanged = false;
        
        if ( m_skipVolumeCount - 1 < 1 )
        {
            float volumeReading = 0;
            for ( int i = 0; i < VOLUME_READ_ATTEMPTS; i++ )
            {
                volumeReading += m_volumeSensor.read();
            }
            volumeReading = volumeReading/VOLUME_READ_ATTEMPTS;
            
            final float newVolume = 1 - volumeReading;
            
            if ( Math.abs( newVolume - m_volume ) > VOLUME_CHANGE_THRESHOLD )
            {
                stateChanged = true;
                m_volume = newVolume;
                
                if ( m_hardwareListener != null ) m_hardwareListener.volumeChanged( newVolume );
            }
            
            m_skipVolumeCount = SKIP_VOLUME_COUNT;
        }
        m_skipVolumeCount -= 1;
        
        return stateChanged;
    }

    private boolean processOnButton() throws InterruptedException, ConnectionLostException
    {
        boolean stateChanged = false;
        
        final boolean isOnNow = m_onSensor.read();
        if ( isOnNow != m_isOn )
        {      
            stateChanged = true;
            m_isOn = isOnNow;
            
            if ( m_hardwareListener != null ) m_hardwareListener.onStateChanged( isOnNow );                        
        }
        
        return stateChanged;
    }

    private boolean processSongRot() throws ConnectionLostException, InterruptedException
    {
        boolean stateChanged = false;
        
        final boolean newRotA = m_rotASensor.read();
        final boolean newRotB = m_rotBSensor.read();
        
        if ( m_rotAOn == true && newRotA == false )
        {
            stateChanged = true;
            
            if ( newRotB )
            {
                m_rotPosition += 1;
            }
            else
            {
                m_rotPosition -= 1;
            }
            
            if ( m_hardwareListener != null ) m_hardwareListener.songSelectorChanged( m_rotPosition );
        }
        
        m_rotAOn = newRotA;
        m_rotBOn = newRotB;
        
        return stateChanged;
    }

    private String buildStateString()
    {
        final StringBuffer result = new StringBuffer("[");
        
        result.append("on: ").append( m_isOn );
        result.append(", volume: ").append( m_volume );
        result.append(", buttons: ");
        for ( int i = 0; i < m_buttonStates.length; i++ )
        {
            result.append( m_buttonStates[i] ? 1 : 0 );
        }
        
        result.append(", selector: ").append( m_rotPosition ).append( ": ");
        result.append( "[" ).append( m_rotAOn ? 1 : 0 ).append( "|" ).append( m_rotBOn ? 1 : 0 );
        
        result.append("]");
        
        return result.toString();
    }

    public interface DebugListener
    {
        void printDebugMessage( final String message );
    }
    
}
